<?php
/************************
 *  Codenova.pl Project
 *  Created by: Kamil Mrzygłód
 *
 *  (c) 2013 by Codenova.pl
 *  All Right Reserved
 ***********************/

namespace Gotcha\App;

// Bootstrap prepares application for start
// by checking files existence and basic
// configuration.

use Gotcha\App\Extensions\ErrorsExtension;
use Gotcha\App\Extensions\FileSystemExtension;
use Gotcha\App\Extensions\CulturesExtension;
use Gotcha\App\Extensions\SecurityExtension;
use Gotcha\App\Types\Arrays;
use Gotcha\App\Types\Int;
use Gotcha\App\Types\String;

class GotchaBootstrap {
    // Error controller preventing further script
    // from being executed
    protected static $fatalError = false;
    protected static $errorHandlerIsSet = false;

    public function __construct() {
        spl_autoload_register(function($class) {
            // Define error
            $classFileDoesNotExist = false;
            $moduleClassFileDoesNotExist = false;

            // We have to define all paths we are searching required files in
            $includePaths = array('/App/Extensions/', '/App/Repositories/', '/App/Database/classes/gotcha/', '/App/Database/classes/gotcha/om/', '/App/Database/classes/gotcha/map/', '/App/Libs/Propel/runtime/lib/util/', '/App/Libs/Propel/runtime/lib/om/', '/App/Libs/Propel/runtime/lib/map/', '/App/Libs/Propel/runtime/lib/query/', '/App/Libs/Propel/runtime/lib/formatter/', '/App/Libs/Propel/runtime/lib/connection/', '/App/Libs/Propel/runtime/lib/exception/', '/App/Libs/Propel/runtime/lib/adapter/', '/App/Libs/Propel/runtime/lib/parser/', '/App/Libs/Propel/runtime/lib/parser/yaml/', '/App/Libs/Propel/runtime/lib/collection/', '/App/Libs/Propel/runtime/lib/config/', '/App/Types/');

            // Extract file name
            $className = explode('\\', $class);

            // Since it is hard to enable dynamic setting of include_path
            // (at least now) there is an easy workaround for this
            // by checking first part of whole namespace

            if($className[0] === 'Modules') {
                $classDir = GOTCHA_ROOT_DIR.'/'.$class.'.php';

                // Namespace should be === module directory
                if(!file_exists($classDir)) {
                    $moduleClassFileDoesNotExist = true;
                } else {
                    $moduleClassFileDoesNotExist = false;
                }
            } else {
                // Check file existence
                foreach ($includePaths as $includePath) {
                    if (!file_exists(GOTCHA_ROOT_DIR.$includePath.$className[count($className)-1].'.php')) {
                        // Define error which prevents system from including file
                        $classFileDoesNotExist = true;
                    } else {
                        $classFileDoesNotExist = false;
                        break; // Break iteration since we found file
                    }
                }
            }

            // Require file
            if (!$classFileDoesNotExist && !isset($classDir)) {
                require_once ($className[count($className)-1].'.php');
            } elseif(!$moduleClassFileDoesNotExist) {
                if(isset($classDir)) {
                    require_once($classDir);
                }
            } else {
                ErrorsExtension::throwError(new String('Given class '.$class.' does not exist.'), new Arrays(array()));
            }
        });
    }

    private function checkRequiredFiles() {
        // We crawl through whole file using SimpleXML.
        // For each node check file existence.

        // Local variables
        $requiredFileName = new String('required.xml');
        $requiredFileDir = new String('App/Config/');
        $fatalError = false;

        if (self::checkForFatalError() === false) {
            if (FileSystemExtension::CheckFileExistence($requiredFileName, $requiredFileDir)) {
                $requiredFiles = simplexml_load_file('App/Config/'.$requiredFileName);

                foreach ($requiredFiles->file as $file) {
                    $requiredFile = new String((string)$file['name']);
                    $requiredFilePath =  new String((string)$file['path']);
                    $requiredFilePermission = new Int((string)$file['permissions']);

                    if (!FileSystemExtension::CheckFileExistence($requiredFile, $requiredFilePath)) {
                        ErrorsExtension::throwErrorAndSaveLogWithoutCulture(new String('REQUIRED_FILE_DOESNT_EXIST'), new Arrays(array()));
                        // Set fatal error to true to prevent application from executing
                        $fatalError = true;
                    } else {
                        // Compare current file permissions and this what we
                        // have in our configuration
                        if($requiredFilePermission->int != substr(decoct(fileperms($requiredFilePath.$requiredFile)), 2)) {
                            chmod($requiredFilePath.$requiredFile, $requiredFilePermission->int);
                        }
                    }
                }
                // Set fatal error visible outside method
                ($fatalError === true) ? self::$fatalError = true : self::$fatalError = false;
            } else {
                ErrorsExtension::throwErrorAndSaveLogWithoutCulture(new String('FILE_DOESNT_EXIST'), new Arrays(array()));
                self::$fatalError = true;
            }
        }

        self::checkForFatalError();
    }

    protected static function checkForFatalError() {
        if (self::$fatalError === true) {
            // Terminate application
            echo CulturesExtension::getTranslation(new String('APPLICATION_INITIALIZATION_CRITICAL_ERROR'));
			exit;
        }

        return false;
    }

    protected static function xmlFileHandler(String $file, String $key)
    {
        return FileSystemExtension::xmlFileHandler(new String('/'.$file), $key);
    }

    public function init() {
        // Set headers
        header ('Content-Type: text/html; charset=UTF-8');

        // We have to enable error handler
        if(!self::$errorHandlerIsSet) {
            $obj = new ErrorsExtension();
            set_error_handler(array($obj, 'gotchaErrorHandler'));
            self::$errorHandlerIsSet = true; // Prevent application from calling handler once more
        }

        self::checkRequiredFiles(); // Check files that were set as required

        // Propel initialization
        require_once GOTCHA_ROOT_DIR.'/App/Libs/Propel/runtime/lib/Propel.php'; // Propel main file
        \Propel::init(GOTCHA_ROOT_DIR.'/App/Database/conf/gotcha-conf.php'); // Propel config

        // Save config from XML to repository
        $fileSystem = new FileSystemExtension();
        $fileSystem->saveConfigToRepository();

        // Check if we accept requests from other domains
        // than the one defined in the config
        if(FileSystemExtension::returnConfigValue(new String('acceptcrossdomainrequests')) != 'true') {
            if(FileSystemExtension::returnConfigValue(new String('trusteddomain')) != '/' && FileSystemExtension::returnConfigValue(new String('trusteddomain')) != '') {
                $requestOrigin = new String($_SERVER['HTTP_HOST']);
                $requestOriginArray = explode(':', $requestOrigin);
                if(FileSystemExtension::returnConfigValue(new String('trusteddomain')) != $requestOriginArray[0]) {
                    ErrorsExtension::throwErrorWithoutLog(new String('NON_TRUSTED_DOMAIN_REQUEST'), new Arrays(array()));
                }
            }
        }

        // Check if there are files, which shouldn't be
        // in application directory if we set
        // production status
        if(FileSystemExtension::returnConfigValue(new String('applicationstatus')) == 'production') {
            // There shouldn't be schema.sql file
            if(FileSystemExtension::CheckFileExistence(new String('schema.sql'), new String(GOTCHA_ROOT_DIR.'\App\Database\sql\\'))) {
                ErrorsExtension::throwNoticeWithoutLog(new String('SCHEMA_SQL_FILE_STILL_EXISTS'), new Arrays(array()));
            }
        }

        // We have to parse tokens file to delete
        // outdated ones
        $security = new SecurityExtension();
        $security->checkSavedFormTokens();
    }
}